using System.Buffers;
using System.Net;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using SocketPorxyServer;
using SuperSocket.Channel;
using SuperSocket.Client;
using SuperSocket.Server;

namespace SuperSocketProxy;

public class SocketProxySession : AppSession
{
    private IEasyClient<SocketProxyPacket> _client;

    internal ushort Port { get; set; }

    internal ProxyPool ProxyPool { get; private set; }

    internal bool IsConnected { get; set; }

    internal IPacketResponse PacketResponse { get; set; }

    internal SocketProxyType ProxyType { get; set; }

    internal EndPoint TargetRemoteEndPoint { get; set; }

    internal readonly CancellationTokenSource CancellationToken = new();

    protected override async ValueTask OnSessionClosedAsync(CloseEventArgs e)
    {
        var client = _client;

        try
        {
            if (client != null)
                await client.CloseAsync();
        }
        finally
        {
            ProxyPool?.EnqueuePort(Port);
        }

        CancellationToken.Cancel();
        CancellationToken.Dispose();
    }

    /// <summary>
    /// 初始化内部设置
    /// </summary>
    internal void Inilizetion()
    {
        ProxyPool = ((IProxyPoolFactory)Server).GetByAddress(((IPEndPoint)LocalEndPoint).Address);

        PacketResponse = ProxyType switch
        {
            SocketProxyType.Http => new HttpPacketResponse(this),
            SocketProxyType.Socket5 => new Socket5PacketResponse(this),
            _ => new Socket6PacketResponse(this)
        };
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    internal ValueTask SendAsync(ReadOnlyMemory<byte> data)
    {
        return Channel.SendAsync(data);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="writer"></param>
    /// <returns></returns>
    internal ValueTask SendAsync(Action<IBufferWriter<byte>> writer)
    {
        return Channel.SendAsync(writer);
    }

    /// <summary>
    /// 是否需要密码   
    /// </summary>
    /// <returns></returns>
    internal bool IsVerify()
    {
        var pool = ProxyPool;

        return !string.IsNullOrWhiteSpace(pool.Username) &&
               !string.IsNullOrWhiteSpace(pool.Password);
    }

    /// <summary>
    /// 验证账号密码
    /// </summary>
    /// <param name="username"></param>
    /// <param name="password"></param>
    /// <returns></returns>
    internal bool VerifyUser(string username, string password)
    {
        var pool = ProxyPool;

        return pool.Username.Equals(username, StringComparison.OrdinalIgnoreCase) &&
               pool.Password.Equals(password, StringComparison.OrdinalIgnoreCase);
    }

    /// <summary>
    /// 发送至目标客户端
    /// </summary>
    /// <param name="body"></param>
    internal async ValueTask SendTargetAsync(List<ArraySegment<byte>> bodys)
    {
        var client = _client;

        if (client == null)
            return;

        foreach (var body in bodys)
        {
            await client.SendAsync(body);
        }
    }

    /// <summary>
    /// 连接目标服务器
    /// </summary>
    /// <param name="remoteEndPoint"></param>
    /// <param name="token"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    /// <exception cref="TimeoutException"></exception>
    /// <exception cref="NotSupportedAddressException"></exception>
    internal async ValueTask<bool> ConnectTargetAsync(EndPoint remoteEndPoint, CancellationToken token)
    {
        if (!ProxyPool.TryGetPort(out var port))
            throw new Exception("可用端口不足");

        bool ssl;
        string targetHost;
        var localEndPoint = (IPEndPoint)LocalEndPoint;

        if (remoteEndPoint is DnsEndPoint dnsEndPoint)
        {
            targetHost = dnsEndPoint.Host;
            ssl = dnsEndPoint.Port == 443;
        }
        else if (remoteEndPoint is IPEndPoint ipEndPoint)
        {
            targetHost = ipEndPoint.Address.ToString();
            ssl = ipEndPoint.Port == 443;
        }
        else
            throw new NotSupportedAddressException("不支持地址类型");

        var client = _client = new EasyClient<SocketProxyPacket>(new ProxyDataPipeFilter())
        {
            LocalEndPoint = new IPEndPoint(localEndPoint.Address, port),
            Security = ssl
                ? new SecurityOptions
                {
                    TargetHost = targetHost,
                    EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13,
                    LocalCertificateSelectionCallback = OnLocalCertificateSelectionCallback,
                    RemoteCertificateValidationCallback = OnRemoteCertificateValidationCallback
                }
                : null
        };

        client.Closed += OnClosed;
        client.PackageHandler += OnPackageReceiveAsync;

        if (!await client.ConnectAsync(remoteEndPoint, token))
            return false;

        //如果连接至目标服务器比较久 而这是客户端与当前客户端已经断开连接 且这时已经连接成功 则需要断开与目标服务器连接
        if (token.IsCancellationRequested)
            return false;

        client.StartReceive();

        return true;
    }

    #region client event

    X509Certificate OnLocalCertificateSelectionCallback(
        object sender,
        string targetHost,
        X509CertificateCollection localCertificates,
        X509Certificate remoteCertificate,
        string[] accepTableIssuers)
    {
        return null;
    }

    bool OnRemoteCertificateValidationCallback(
        object sender,
        X509Certificate certificate,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
    {
        return true;
    }

    async void OnClosed(object sender, EventArgs e)
    {
        await CloseAsync();
    }

    async ValueTask OnPackageReceiveAsync(EasyClient<SocketProxyPacket> sender, SocketProxyPacket package)
    {
        var data = (SocketProxyDataPacket)package;

        foreach (var buffer in data.Body)
        {
            await Channel.SendAsync(buffer);
        }
    }

    #endregion
}